import json
from pathlib import Path

import uvicorn
from fastapi import APIRouter, Cookie, Depends, FastAPI, HTTPException, Response
from fastapi.staticfiles import StaticFiles
from jose import JWTError, jwt
from playhouse.shortcuts import model_to_dict
from pydantic import BaseModel

from awesomest_spider import glb, model, spiders

config: dict = glb.config['server']
app = FastAPI()
# 加载 src 中的文件
# method: GET
# URL: localhost:10025/src/login.html
app.mount('/src', StaticFiles(directory=Path('awesomest_spider', 'src')))


# 接收输入的模型基类
class Model(BaseModel):
    class Config:
        anystr_strip_whitespace = True


def set_token(user, response):
    response.set_cookie(
        key='token', value=jwt.encode(
            {
                'username': user.username,
                'password': user.password,
                'visible_spider': json.loads(user.visible_spider),
            },
            key=config['jwt']['key'], algorithm=config['jwt']['algorithm']
        )
    )


# 用户信息的输入模型
class User(Model):
    username: str
    password: str


# 登录接口
# method: POST
# URL: localhost:10025/api/login
@app.post('/api/login')
def login(user_in: User, response: Response):
    user = model.User.get_or_none((model.User.username == user_in.username) & (model.User.password == user_in.password))
    if user:
        set_token(user, response)
    else:
        return Response(status_code=400)


# 注册接口
@app.post('/api/sign_in')
def sign_in(user_in: User, response: Response):
    user = model.User.create(
        username=user_in.username,
        password=user_in.password,
        visible_spider=json.dumps(['bilibili', 'douban', 'jd', 'netease']),
    )
    set_token(user, response)


# 返回 token 的内容
async def get_token(token=Cookie(...)) -> dict:
    """
    :param token: 即 cookie 中的 token 的值
    """
    try:
        return jwt.decode(token, key=config['jwt']['key'], algorithms=[config['jwt']['algorithm']])
    except JWTError:
        raise HTTPException(status_code=403, detail='Token Invalid')


# 确保已经登录的 router
router = APIRouter(prefix='/api', dependencies=[Depends(get_token)])


class UserUpdate(Model):
    username: str
    password: str
    visible_spider: str


# 修改用户信息的接口
@router.post('/update_user')
def update_user(user: UserUpdate):
    model.User.update(password=user.password, visible_spider=user.visible_spider) \
        .where(model.User.username == user.username).execute()


# 用户展示接口
@router.get('/list_user')
def list_user():
    return [model_to_dict(i) for i in model.User.select()]


# 删除用户接口
@router.delete('/delete_user/{d_user}')
def delete_user(d_user):
    model.User.delete().where(model.User.username == d_user).execute()


# 设置可见爬虫的输入模型
class VisibleSpiderSet(Model):
    username: str
    visible_spider: str


# 爬虫结果展示
@router.get('/list_data/{spider}')
def list_data(spider, token=Depends(get_token)):
    if spider in token['visible_spider']:
        return [model_to_dict(i) for i in getattr(spiders, spider).model.select()]


# 搜索框模型
class SpiderStart(Model):
    spider: str
    query: dict


# 开始爬虫接口
@router.post('/start_spider')
def start_spider(spider: SpiderStart, token=Depends(get_token)):
    if spider.spider in token['visible_spider']:
        return tuple(getattr(spiders, spider.spider).main(**spider.query))


def run():
    app.include_router(router)
    uvicorn.run(app, host='0.0.0.0', port=config['port'], log_level=glb.log_level)
